﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using DCT.ViewModel;

namespace DCT.View
{
    public class DeviceCanvas : Canvas
    {
        #region Constructor

        public DeviceCanvas()
        {
            this.DataContextChanged += DeviceCanvas_DataContextChanged;
            this.MouseDown += DeviceCanvas_OnMouseDown;
            this.MouseMove += DeviceCanvas_OnMouseMove;
            this.MouseUp += DeviceCanvas_OnMouseUp;
            this.MouseWheel += DeviceCanvas_OnMouseWheel;
            this.Loaded += DeviceCanvas_Loaded;
            this.LayoutTransform = new ScaleTransform();
        }

        #endregion //Constructor

        #region Fields

        private Point? dragStartPoint;
        private Point? moveStartPoint;
        private Point clickStartPoint;
        private DeviceMapViewModel _deviceMapViewModel;

        #endregion // Fields

        #region Properties

        public ScrollViewer ScrollViewer
        {
            get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
            set { SetValue(ScrollViewerProperty, value); }
        }

        public static readonly DependencyProperty ScrollViewerProperty =
            DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(Canvas));

        public DeviceMapViewModel DeviceMapViewModel
        {
            get
            {
                return _deviceMapViewModel;
            }
        }

        #endregion // Properties

        #region Methods

        void DeviceCanvas_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (this.DataContext is DeviceMapViewModel)
            {
                _deviceMapViewModel = this.DataContext as DeviceMapViewModel;
                _deviceMapViewModel.CanvasInvalidateMeasureHandler += this.HandleCanvasInvalidateMeasure;
                _deviceMapViewModel.DrawEndHander += this.HandleDrawEnd;
                _deviceMapViewModel.CanvasLoactionHandler += this.CanvasLoaction;
            }
        }

        void HandleCanvasInvalidateMeasure(object sender, EventArgs e)
        {
            this.InvalidateMeasure();
        }

        void HandleDrawEnd(object sender, EventArgs e)
        {
            if (this.IsMouseCaptured)
            {
                this.ReleaseMouseCapture();
            }
        }

        void CanvasLoaction(Point pt)
        {
            ScaleTransform scaleTransform = this.LayoutTransform as ScaleTransform;
            if (scaleTransform == null)
            {
                scaleTransform = new ScaleTransform();
                this.LayoutTransform = scaleTransform;
            }

            this.ScrollViewer.ScrollToHorizontalOffset((pt.X * scaleTransform.ScaleX) - this.ScrollViewer.ViewportWidth / 2);
            this.ScrollViewer.ScrollToVerticalOffset((pt.Y * scaleTransform.ScaleY) - this.ScrollViewer.ViewportHeight / 2);
        }

        void DeviceCanvas_OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                if (_deviceMapViewModel.EditMode == EditModeState.DrawSwitch || _deviceMapViewModel.EditMode == EditModeState.DrawTrackSegment)
                {
                    this.clickStartPoint = e.GetPosition(this);
                }
                else
                {
                    this.dragStartPoint = new Point?(e.GetPosition(this));
                }

                this._deviceMapViewModel.DeselectAll_Execute();
            }

            if (e.ChangedButton == MouseButton.Middle)
            {
                this.Cursor = Cursors.Hand;
                if (!this.IsMouseCaptured)
                {
                    this.CaptureMouse();
                }
                this.moveStartPoint = new Point?(e.GetPosition(this));
            }
        }

        void DeviceCanvas_OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed)
            {
                this.dragStartPoint = null;
            }

            if (e.MiddleButton != MouseButtonState.Pressed)
            {
                this.moveStartPoint = null;
            }

            if (this.dragStartPoint.HasValue)
            {
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                if (adornerLayer != null)
                {
                    RubberbandAdorner adorner = new RubberbandAdorner(this, this.dragStartPoint);
                    if (adorner != null)
                    {
                        adornerLayer.Add(adorner);
                    }
                }
            }

            if (this.moveStartPoint.HasValue)
            {
                Point curPoint = e.GetPosition(this);
                ScaleTransform scaleTransform = this.LayoutTransform as ScaleTransform;
                if (scaleTransform == null)
                {
                    scaleTransform = new ScaleTransform();
                    this.LayoutTransform = scaleTransform;
                }
                this.ScrollViewer.ScrollToHorizontalOffset(this.ScrollViewer.HorizontalOffset + ((this.moveStartPoint.Value.X - curPoint.X) * scaleTransform.ScaleX));
                this.ScrollViewer.ScrollToVerticalOffset(this.ScrollViewer.VerticalOffset + ((this.moveStartPoint.Value.Y - curPoint.Y) * scaleTransform.ScaleY));
            }

            if(_deviceMapViewModel.TrackSegmentEnd != null)
            {
                _deviceMapViewModel.TrackSegmentEnd.PositionPoint = e.GetPosition(this);
            }

            if (_deviceMapViewModel.SwitchNormal != null)
            {
                _deviceMapViewModel.SwitchNormal.PositionPoint = e.GetPosition(this);
            }

            if (_deviceMapViewModel.SwitchReverse != null)
            {
                _deviceMapViewModel.SwitchReverse.PositionPoint = e.GetPosition(this);
            }
        }

        void DeviceCanvas_OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                this.dragStartPoint = null;

                if (e.GetPosition(this) == this.clickStartPoint)
                {
                    if (_deviceMapViewModel.DrawTrackSegmentBegin_Can(this.clickStartPoint))
                    {
                        if (!this.IsMouseCaptured)
                        {
                            this.CaptureMouse();
                        }
                        _deviceMapViewModel.DrawTrackSegmentBegin_Execute(this.clickStartPoint);
                    }
                    else if (_deviceMapViewModel.DrawTrackSegmentEnd_Can())
                    {
                        _deviceMapViewModel.DrawTrackSegmentEnd_Execute(true);

                        if ((Keyboard.Modifiers & (ModifierKeys.Control)) != ModifierKeys.None)
                        {
                            if (_deviceMapViewModel.DrawTrackSegmentBegin_Can(this.clickStartPoint))
                            {
                                _deviceMapViewModel.DrawTrackSegmentBegin_Execute(this.clickStartPoint);
                            }
                        }
                    }

                    if (_deviceMapViewModel.DrawSwitchMerge_Can(this.clickStartPoint))
                    {
                        if (!this.IsMouseCaptured)
                        {
                            this.CaptureMouse();
                        }
                        _deviceMapViewModel.DrawSwitchMerge_Execute(this.clickStartPoint);
                    }
                    else if (_deviceMapViewModel.DrawSwitchNormal_Can())
                    {
                        _deviceMapViewModel.DrawSwitchNormal_Execute(true);
                    }
                    else if (_deviceMapViewModel.DrawSwitchReverse_Can())
                    {
                        _deviceMapViewModel.DrawSwitchReverse_Execute(true);
                    }
                }
            }

            if (e.ChangedButton == MouseButton.Middle)
            {
                Cursor = Cursors.Arrow;

                if (this.IsMouseCaptured)
                {
                    this.ReleaseMouseCapture();
                }
                this.moveStartPoint = null;
            }

            if (e.ChangedButton == MouseButton.Right)
            {
                if (_deviceMapViewModel.DrawTrackSegmentEnd_Can())
                {
                    _deviceMapViewModel.DrawTrackSegmentEnd_Execute(false);
                }

                if (_deviceMapViewModel.DrawSwitchNormal_Can())
                {
                    _deviceMapViewModel.DrawSwitchNormal_Execute(false);
                }

                if (_deviceMapViewModel.DrawSwitchReverse_Can())
                {
                    _deviceMapViewModel.DrawSwitchReverse_Execute(false);
                }
            }
        }

        void DeviceCanvas_OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            ScaleTransform scaleTransform = this.LayoutTransform as ScaleTransform;

            double scale = e.Delta < 0 ? 0.5 : 2;

            scaleTransform.ScaleX = Math.Max(scaleTransform.ScaleX * scale, 0.125);
            scaleTransform.ScaleY = Math.Max(scaleTransform.ScaleY * scale, 0.125);

            this.CanvasLoaction(e.GetPosition(this));
        }

        void DeviceCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            DependencyObject dp = this;

            while (dp != null && !(dp is DeviceMapView))
            {
                dp = VisualTreeHelper.GetParent(dp);
            }

            DeviceMapView deviceMapView = dp as DeviceMapView;
            if (deviceMapView != null)
            {
                deviceMapView.DeviceCanvas = this;
            }
        }

        #region Overrides

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size();
            foreach (UIElement element in Children)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;

                element.Measure(constraint);

                Size desiredSize = element.DesiredSize;
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    size.Width = Math.Max(size.Width, left + desiredSize.Width);
                    size.Height = Math.Max(size.Height, top + desiredSize.Height);
                }
            }

            // add some extra margin
            size.Width += 10;
            size.Height += 10;
            return size;
        }

        #endregion // Overrides

        #endregion // Methods
    }
}
